using System;
using System.Collections;
using System.Text;

namespace Patchy
{
    /// <summary>
    /// Summary description for MinDirName.
    /// </summary>
    /// TODO: implement aliases
    public class MinDirName
    {
        private SortedList mLookup = new SortedList(StringComparer.OrdinalIgnoreCase);
        private SortedList mCombinedAliases = new SortedList(StringComparer.OrdinalIgnoreCase);
        private GroupHolder mGroupHolder;
        private int AliasingNumber = 0;

        public MinDirName(GroupHolder groupHolder)
        {
            mGroupHolder = groupHolder;
        }

        public void Process()
        {
            ArrayList DirNames = new ArrayList();
            foreach (MyGroup group in mGroupHolder.SortedGroupCollection)
            {
                DirNames.Add(new DirNameRepr(group.DirectoryName));
            }
            DirNames.Sort();
            PackDirs(DirNames, 0);
            mGroupHolder.AliasTable = mLookup;
            foreach (MyGroup group in mGroupHolder.SortedGroupCollection)
            {
                group.AliasCombined = mCombinedAliases[group.DirectoryName].ToString();
            }
        }

        /// <summary>
        /// This function goes through a sorted list of directories, and sets alias values
        /// as follows: directories are grouped by drive. Each such set is then tested to see
        /// if they are the same further along their paths. Above the first directory in their
        /// paths, the deeepest match is sufficient even if this is simply just that (i.e. the
        /// set isn't divided up further.)
        /// </summary>
        private void PackDirs(ArrayList ar, int level)
        {
            if (ar.Count == 1)
            {
                SetHighestMatches(ar, level);
            }
            else
            {
                for (int i = 1; i < ar.Count; i++)
                {
                    if (((DirNameRepr)ar[i - 1]).LevelCompare((DirNameRepr)ar[i], level))
                    {
                        continue;
                    }
                    if (level <= 1)
                    {
                        if (i == 1)
                            PackDirs(ar.GetRange(0, i), level); //single item
                        else
                            PackDirs(ar.GetRange(0, i), level + 1); //group of items
                        PackDirs(ar.GetRange(i, ar.Count - i), level); //unevaluated so far
                    }
                    else
                    {
                        SetHighestMatches(ar.GetRange(0, ar.Count - i), level);
                    }
                    return;
                }
                //whole group is a match
                if (level == 0)
                {
                    PackDirs(ar, level + 1);
                }
                else
                {
                    SetHighestMatches(ar, level);
                }
            }
        }

        private void SetHighestMatches(ArrayList ar, int baseLevel)
        {
            int level;
            if (ar.Count == 1)
            {
                level = ((DirNameRepr)ar[0]).segNumber + 1;
            }
            else
            {
                bool cont = true;
                for (level = baseLevel; cont; level++)
                {
                    for (int j = 1; j < ar.Count; j++)
                    {
                        if (!((DirNameRepr)ar[j - 1]).LevelCompare((DirNameRepr)ar[j], level))
                        {
                            cont = false;
                            level--;
                            break;
                        }
                    }
                }
            }
            AliasingNumber++;
            foreach (DirNameRepr d in ar)
            {
                int startIndex = d.GetStartSegment(level).Length;
                string directoryN = string.Format(@"DIRECTORY{0}", AliasingNumber);
                string alias = string.Format(@"%{0}%{1}", directoryN, d.InitialName.Substring(startIndex));
                if (!mLookup.Contains(directoryN))
                {
                    mLookup.Add(directoryN, d.GetStartSegment(level));
                }
                if(!mCombinedAliases.Contains(d.InitialName))
                {
                    mCombinedAliases.Add(d.InitialName, alias);
                }
            }
        }
    }

    class DirNameRepr : IComparable
    {
        ArrayList segs;
        public int segNumber;
        string scrubbedName;
        string initialName;

        public DirNameRepr(string fname)
        {
            initialName = fname;
            scrubbedName = initialName.ToLower().Trim('\\');
            segs = new ArrayList(scrubbedName.Split('\\'));
            segNumber = segs.Count - 1;
        }

        public bool LevelCompare(DirNameRepr other, int level)
        {
            return other != null &&
                (this.segNumber >= level && other.segNumber >= level) &&
                (this.segs[level].ToString() == other.segs[level].ToString());
        }

        public string InitialName
        {
            get { return initialName; }
        }

        public string GetStartSegment(int level)
        {
            string[] sa = initialName.Trim().Split('\\');
            string s = sa[0].ToUpper() + '\\';
            for (int i = 1; i < level; i++)
            {
                s += sa[i] + '\\';
            }
            return s;
        }

        #region IComparable Members
        public int CompareTo(object obj)
        {
            DirNameRepr d = (DirNameRepr)obj;
            return this.scrubbedName.CompareTo(d.scrubbedName);
        }
        #endregion
    }
}
